﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using MyFramework.Common.Enums;
using MyFramework.Common.Interfaces;

namespace MyFramework.Core
{
    /// <summary>
    /// Controller factory base
    /// </summary>
    public abstract class ControllerFactoryBase : IControllerFactory
    {
        private SynchronizationContext syncContext;

        /// <summary>
        /// Initializes a new instance of the <see cref="ControllerFactoryBase"/> class.
        /// </summary>
        protected ControllerFactoryBase()
        {
            syncContext = SynchronizationContext.Current;
        }

        /// <summary>
        /// Creates the controller instance.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="initParameters">The initialize parameters.</param>
        /// <returns></returns>
        protected abstract IController CreateControllerInstance(Type type, IDictionary<string, object> initParameters);

        /// <summary>
        /// Creates the controller core.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="initParameters">The initialize parameters.</param>
        /// <param name="onCreatedAction">The on created action.</param>
        /// <param name="onInitializedAction">The on initialized action.</param>
        /// <param name="onException">The on exception.</param>
        /// <exception cref="System.InvalidOperationException"></exception>
        private void CreateControllerCore(Type type, IDictionary<string, object> initParameters, Action<IController> onCreatedAction, Action<IController> onInitializedAction, Action<Exception> onException)
        {
            IController controller = null;
            try
            {
                controller = CreateControllerInstance(type, initParameters);

                Debug.WriteLine(" °°°°°°°°°°°° ControllerFactoryBase --> " + type + " : State --> N/A ");

                if (controller == null)
                {
                    throw new InvalidOperationException(Resources.CreateControllerInstanceImplementationCannotReturnNull);
                }

                controller.Type = type;
                if (onCreatedAction != null)
                {
                    onCreatedAction(controller);
                    Debug.WriteLine(" °°°°°°°°°°°° ControllerFactoryBase --> " + type + " : State --> onCreatedAction");
                }

                controller.ExecuteWhen<IController>(
                    (propertyName, c) => propertyName == "State" && c.State == ControllerState.Initialized,
                    c =>
                    {
                        syncContext.Post(state1 =>
                        {
                            if (onInitializedAction != null)
                            {
                                onInitializedAction(c);
                                Debug.WriteLine(" °°°°°°°°°°°° ControllerFactoryBase --> " + type + " : State --> onInitialized");
                            }

                            if (c.State < ControllerState.Disposing)
                            {

                                Debug.WriteLine(" °°°°°°°°°°°° ControllerFactoryBase --> " + type + " : State --> DoStartupAction executing");
                                c.DoStartupAction();
                                Debug.WriteLine(" °°°°°°°°°°°° ControllerFactoryBase --> " + type + " : State --> DoStartupAction executed");

                                c.State = ControllerState.Ready;
                            }

                        }, null);
                    },
                    true);

                controller.Initialize(initParameters);
                Debug.WriteLine(" °°°°°°°°°°°° ControllerFactoryBase --> " + type + " : State --> Initialize");

            }
            catch (Exception ex)
            {
                try
                {
                    if (controller != null)
                    {
                        controller.Dispose();
                    }
                }
                catch (Exception)
                {
                }

                var ex1 = new ControllerException(Resources.ExceptionInControllerFactory.FormatEx(type), Guid.Empty, null, ex);
                if (onException != null)
                {
                    onException(ex1);
                }
                else if (DefaultExceptionHandler != null)
                {
                    DefaultExceptionHandler(ex1);
                }
                else
                {
                    throw ex1;
                }
            }
        }

        #region IControllerFactory Members

        /// <summary>
        /// Creates the controller.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="initParameters">The initialize parameters.</param>
        /// <param name="onCreatedAction">The on created action.</param>
        /// <param name="onInitializedAction">The on initialized action.</param>
        /// <param name="onException">The on exception.</param>
        public void CreateController(Type type, IDictionary<string, object> initParameters, Action<IController> onCreatedAction, Action<IController> onInitializedAction, Action<Exception> onException)
        {
            syncContext.Post(state =>
            {
                CreateControllerCore(type, initParameters, onCreatedAction, onInitializedAction, onException);
            }, null);
        }

        /// <summary>
        /// Creates the controller synchronize.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="initParameters">The initialize parameters.</param>
        /// <param name="onCreatedAction">The on created action.</param>
        /// <param name="onInitializedAction">The on initialized action.</param>
        /// <param name="onException">The on exception.</param>
        public void CreateControllerSync(Type type, IDictionary<string, object> initParameters, Action<IController> onCreatedAction, Action<IController> onInitializedAction, Action<Exception> onException)
        {
            syncContext.Send(state =>
            {
                CreateControllerCore(type, initParameters, onCreatedAction, onInitializedAction, onException);
            }, null);
        }

        #endregion

        /// <summary>
        /// The default exception handler
        /// </summary>
        public static Action<Exception> DefaultExceptionHandler;


        #region IModule Members

        /// <summary>
        /// Notifies the module that it has be initialized.
        /// </summary>
        public void Initialize()
        {

        }

        #endregion
    }
}
